using System;
using System.Collections.Generic;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class represents a collection of linear factors.
    /// </summary>
    [Serializable]
    public class LinearFactorCollection : AbstractCollection
    {
        /// <summary>
        /// The list of linear factors.
        /// </summary>
        private readonly List<LinearFactor> collection;

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearFactorCollection"/> class.
        /// </summary>
        public LinearFactorCollection()
        {
            this.collection = new List<LinearFactor>();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearFactorCollection"/> class.
        /// </summary>
        /// <param name="collection">An array of linear factors.</param>
        public LinearFactorCollection(LinearFactor[] collection)
        {
            if (collection == (LinearFactor[]) null)
            {
                throw new ArgumentNullException("collection");
            }

            this.collection = new List<LinearFactor>();

            foreach (LinearFactor f in collection)
            {
                this.collection.Add(f);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearFactorCollection"/> class.
        /// </summary>
        /// <param name="collection">A list of linear factors.</param>
        public LinearFactorCollection(List<LinearFactor> collection)
        {
            if (collection == (List<LinearFactor>) null)
            {
                throw new ArgumentNullException("collection");
            }

            this.collection = collection;
        }

        /// <summary>
        /// Adds an element to the current collection.
        /// </summary>
        /// <param name="factor">The linear factor to add.</param>
        public void Add(LinearFactor factor)
        {
            this.collection.Add(factor);
        }

        /// <summary>
        /// Inserts a linear factor at the specified index.
        /// </summary>
        /// <param name="index">The specified position.</param>
        /// <param name="value">The value to insert.</param>
        public void Insert(int index, LinearFactor value)
        {
            this.collection.Insert(index, value);
        }

        /// <summary>
        /// Removes an element to the current collection.
        /// </summary>
        /// <param name="index">The index.</param>
        public void RemoveAt(int index)
        {
            this.collection.RemoveAt(index);
        }

        /// <summary>
        /// Removes all items from the current collection.
        /// </summary>
        public void Clear()
        {
            this.collection.Clear();
        }

        /// <summary>
        /// Gets an linear factor element at a specified position.
        /// </summary>
        /// <param name="index">The specified position.</param>
        /// <returns>The linear factor at the specified position.</returns>
        public LinearFactor GetElementAt(int index)
        {
            return this.collection[index];
        }

        /// <summary>
        /// Checks if a specified value in the collection exists.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>True if the specified value exists in the collection otherwise, false.</returns>
        public bool Exist(LinearFactor value)
        {
            foreach (LinearFactor factor in this.collection)
            {
                if (factor == value)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Multiplies all linear factors of the collection.
        /// </summary>
        /// <returns>A polynomial of all multiplied linear factors.</returns>
        public SimplePolynomial MultiplyFactors()
        {
            if (this.collection.Count > 1)
            {
                SimplePolynomial p = this.collection[0].ToPolynomial();

                for (int i = 1; i < this.collection.Count; i++)
                {
                    p *= this.collection[i].ToPolynomial();
                }

                return p;
            }

            throw new IllegalArithmeticException(
                "For a multiplication process a minimum of two linear factors are needed.");
        }

        /// <summary>
        /// Gets the collection of linear factors.
        /// </summary>
        /// <value>The collection of linear factors.</value>
        public List<LinearFactor> Value
        {
            get { return collection; }
        }

        /// <summary>
        /// Gets the number of items, which the collection includes.
        /// </summary>
        /// <value>The number of items, which the collection includes.</value>
        public int Count
        {
            get { return collection.Count; }
        }

        /// <summary>
        /// Copies the current instance.
        /// </summary>
        /// <returns>A copy of the current instance.</returns>
        public LinearFactorCollection Copy()
        {
            return new LinearFactorCollection(this.ToArray());
        }

        /// <summary>
        /// Compares the current instance to another.
        /// </summary>
        /// <param name="compare">The collection to compare.</param>
        /// <returns>True if the collection values and the order of the items are even otherwise, false.</returns>
        public bool CompareTo(LinearFactorCollection compare)
        {
            return this == compare;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first LinearFactorCollection.</param>
        /// <param name="b">The second LinearFactorCollection.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(LinearFactorCollection a, LinearFactorCollection b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object) a == null) || ((object) b == null))
            {
                return false;
            }

            for (int i = 0; i < a.Count; i++)
            {
                if (a.GetElementAt(i) != b.GetElementAt(i))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">The first LinearFactorCollection.</param>
        /// <param name="b">The second LinearFactorCollection.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(LinearFactorCollection a, LinearFactorCollection b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Gets or sets the <see cref="SmartMathLibrary.LinearFactor"/> at the specified index.
        /// </summary>
        /// <value>The LinearFactor value.</value>
        public LinearFactor this[int index]
        {
            get { return this.collection[index]; }
            set { this.collection[index] = value; }
        }

        /// <summary>
        /// Gets the enumerator.
        /// </summary>
        /// <returns>The enumerator of the current iteration.</returns>
        public IEnumerator<LinearFactor> GetEnumerator()
        {
            for (int i = 0; i < this.collection.Count; i++)
            {
                yield return this.collection[i];
            }
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Converts the current collection to an array.
        /// </summary>
        /// <returns>The converted array.</returns>
        public LinearFactor[] ToArray()
        {
            return this.collection.ToArray();
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return base.ToString();
        }
    }
}